﻿using System;
using System.Data.Linq;
using System.Data;
using System.Data.Linq.Mapping;

namespace StockholmsStad.Data
{
    [DatabaseAttribute(Name = "StockholmsStad")]
    public partial class DataContextBase : DataContext, IDisposable
    {
        #region Fields
        partial void OnCreated();
        partial void OnTransactionCreated();
        #endregion

        #region Properties

        protected bool IsDisposed { get; private set; }
        private IsolationLevel TransactionIsolationLevel { get; set; }

        #endregion

        #region Constructor

        public DataContextBase(string connectionString) : base(connectionString) { }

        public DataContextBase(string connectionString, MappingSource mapping) : base(connectionString, mapping) { }
        public DataContextBase(IDbConnection connection) : base(connection) { }

        public DataContextBase(IDbConnection connection, MappingSource mapping) : base(connection, mapping) { }

        public DataContextBase(string connectionString, IsolationLevel isolationLevel, MappingSource mapping)
            : this(connectionString, mapping)
        {
            if (connectionString == null)
                throw new ArgumentNullException("connectionString");
            TransactionIsolationLevel = isolationLevel;
            Connection.StateChange += OnConnectionStateChange;
            OnCreated();
        }

        public DataContextBase(string connectionString, IsolationLevel level) : this(connectionString, level, new AttributeMappingSource()) { }

        #endregion

        private void OnConnectionStateChange(object sender, StateChangeEventArgs e)
        {
            if (e.CurrentState == ConnectionState.Open && e.OriginalState == ConnectionState.Closed)
            {
                BeginTransaction(TransactionIsolationLevel);
            }

            if (e.CurrentState == ConnectionState.Closed && e.OriginalState == ConnectionState.Open)
            {
                CommitTransaction();
            }
        }

        private void BeginTransaction(IsolationLevel isolationLevel)
        {
            OpenConnection();
            if (Transaction == null)
            {
                Transaction = Connection.BeginTransaction();
                OnTransactionCreated();
            }
        }

        private void CommitTransaction()
        {
            if (!IsDisposed && Transaction != null && Transaction.Connection != null)
            {
                try
                {
                    Transaction.Commit();
                }
                catch (Exception)
                {
                    Transaction.Rollback();
                    throw;
                }
                finally
                {
                    Transaction.Dispose();
                    Transaction = null;
                }
            }
        }

        private void OpenConnection()
        {
            if (!IsDisposed && Connection.State == ConnectionState.Closed)
            {
                Connection.Open();
            }
        }

        private void EnsureTransaction()
        {
            if (Transaction == null)
            {
                BeginTransaction(TransactionIsolationLevel);
            }
        }

        private void CloseConnection()
        {
            if (!IsDisposed && Connection.State == ConnectionState.Open)
            {
                Connection.Close();
            }
        }

        public new void SubmitChanges()
        {
            SubmitChanges(ConflictMode.FailOnFirstConflict);
        }

        private new void SubmitChanges(ConflictMode failureMode)
        {
            try
            {
                EnsureTransaction();
                base.SubmitChanges(failureMode);
                CommitTransaction();
                CloseConnection();
            }
            catch (Exception)
            {
                throw;
            }
        }

        #region IDisposable Members

        protected void CheckDispose()
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException("DBContextBase");
            }
        }

        public new void Dispose(bool disposing)
        {
            if (!IsDisposed && disposing)
            {
                CommitTransaction();
                CloseConnection();
                Dispose();
                IsDisposed = true;
            }
        }

        void IDisposable.Dispose()
        {
            Dispose(true);

            // Suppress finalization of this disposed instance.
            GC.SuppressFinalize(this);
        }

        ~DataContextBase()
        {
            Dispose(false);
        }

        #endregion
    }
}
